package com.ehotting.edsta.file.controller;

import com.alibaba.fastjson.JSONObject;
import com.ehotting.eaf.core.domain.R;
import com.ehotting.eaf.core.domain.system.api.SysFile;
import com.ehotting.eaf.core.exception.ServiceException;
import com.ehotting.eaf.core.utils.StringUtils;
import com.ehotting.eaf.core.utils.file.FileUtils;
import com.ehotting.eaf.security.service.TokenService;
import com.ehotting.edsta.file.service.ISysFileService;
import com.ehotting.edsta.file.utils.MinioUtils;
import io.minio.MinioClient;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

/**
 * 文件请求处理
 * 文件上传：
 * 配置url：http://127.0.0.1:19100/file/download?filepath=
 * 以和前端就需要组装token获取文件流
 * 其他模块程序仅提供临时访问的url（具体时效依据token），此url通常由以和程序（C端/后端）组装
 * 
 * @author edsta
 */
@RestController
@Api(tags = "SysFileController",description = "文件请求处理")
public class SysFileController
{
    private static final Logger log = LoggerFactory.getLogger(SysFileController.class);

    @Autowired
    private ISysFileService sysFileService;

    @Autowired
    private MinioClient client;

    @Autowired
    private TokenService tokenService;

    @Value("${path.version}")
    private String versionPath;

    /**
     * 上传电路文件（批量上传）
     */
    @ApiOperation("文件上传请求")
    @PostMapping("/uploadCircuitDirectory")
    public R uploadCircuitFiles(@RequestParam(name = "file") MultipartFile file, String relativePath) {
        String dirName = relativePath.substring(0,relativePath.indexOf("/"));
        if(!dirName.equals("circuitFiles")){
            log.error("请选择正确的上传目录（circuitFiles）：{}",dirName);
            return R.fail("请选择正确的上传目录（circuitFiles）：{}",dirName);
        }
        try {
            String replace = relativePath.replace("circuitFiles/", "");
            String url = sysFileService.uploadCircuitFile(file, replace);
            SysFile sysFile = new SysFile();
            sysFile.setName(FileUtils.getName(url));
            sysFile.setUrl(url);
            log.info("uploadCircuitDirectory:{}\n\n", JSONObject.toJSONString(sysFile));
            return R.ok(sysFile);
        } catch (Exception e) {
            log.error("上传文件失败", e);
            return R.fail(e.getMessage());
        }
    }

    /**
     * 文件上传请求
     * TODO ALTER TABLE `edsta`.`sys_user` MODIFY COLUMN `avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '头像地址' AFTER `sex`;
     */
    @ApiOperation("文件上传请求")
    @PostMapping("upload")
    public R<SysFile> upload(MultipartFile file)
    {
        try
        {
            // 上传并返回访问地址
            String url = sysFileService.upload(file);
            SysFile sysFile = new SysFile();
            sysFile.setName(FileUtils.getName(url));
            sysFile.setUrl(url);
            return R.ok(sysFile);
        }
        catch (Exception e)
        {
            log.error("上传文件失败", e);
            return R.fail(e.getMessage());
        }
    }

    /**
     * 文件上传请求
     */
    @ApiOperation("文件上传请求")
    @PostMapping("uploadByFilepath")
    public R<SysFile> uploadByFilepath(@RequestParam("file") MultipartFile file,
                             @RequestParam("bucketName") String bucketName,
                             @RequestParam(name = "filepath",required = false,defaultValue = "") String filepath) {
        try {
            // 上传并返回访问地址
            String url = sysFileService.uploadByFilepath(file,bucketName,filepath);
            SysFile sysFile = new SysFile();
            sysFile.setName(FileUtils.getName(url));
            sysFile.setUrl(url);
            return R.ok(sysFile);
        }
        catch (Exception e) {
            log.error("上传文件失败", e);
            return R.fail(e.getMessage());
        }
    }

    /**
     * 文件上传请求
     */
    @ApiOperation("文件上传请求")
    @PostMapping("uploadAllFiles")
    public R<String> uploadAllFiles(@RequestParam("files[]") List<MultipartFile> files,
                             @RequestParam("bucketName") String bucketName,
                             @RequestParam("filepath") String filepath) {
        try {
            // 上传并返回访问地址
            String url = sysFileService.uploadFiles(files,bucketName,filepath,false);
    //            SysFile sysFile = new SysFile();
    //            sysFile.setName(FileUtils.getName(url));
    //            sysFile.setUrl(url);
            return R.ok(url);
        }
        catch (Exception e) {
            log.error("上传文件失败", e);
            return R.fail(e.getMessage());
        }
    }

    /**
     * 文件上传请求
     */
    @ApiOperation("文件上传请求")
    @PostMapping("uploadAllFiles2")
    public R<String> uploadAllFiles2(@RequestParam("files[]") List<MultipartFile> files,
                                    @RequestParam("bucketName") String bucketName,
                                    @RequestParam("filepath") String filepath) {
        try {
            // 上传并返回访问地址
            String url = sysFileService.uploadFiles(files,bucketName,filepath,true);
            //            SysFile sysFile = new SysFile();
            //            sysFile.setName(FileUtils.getName(url));
            //            sysFile.setUrl(url);
            return R.ok(url);
        }
        catch (Exception e) {
            log.error("上传文件失败", e);
            return R.fail(e.getMessage());
        }
    }

    /**
     * 文件上传请求
     */
    @ApiOperation("文件上传请求")
    @PostMapping("/uploadByModule")
    public R<SysFile> uploadByModule(@RequestParam("file") MultipartFile file,
                                     @RequestParam("moduleName") String moduleName,
                                     @RequestParam("accessKey") String accessKey,
                                     @RequestParam("secretKey") String secretKey) {
        try {
            // 上传并返回访问地址
            String url = sysFileService.uploadFileByModule(file,moduleName,accessKey,secretKey);
            SysFile sysFile = new SysFile();
            sysFile.setName(FileUtils.getName(url));
            sysFile.setUrl(url);
            return R.ok(sysFile);
        }
        catch (Exception e)
        {
            log.error("上传文件失败", e);
            return R.fail(e.getMessage());
        }
    }

    @ApiOperation("获取文件二进制流")
    @GetMapping("/getBytesFromMinio")
    public R<byte[]> getBytesFromMinio(@RequestParam("filename") String filename,
                                       @RequestParam("moduleName") String moduleName){

        MinioUtils minioUtils = new MinioUtils(client, moduleName);
        return R.ok(minioUtils.getBytesFromMinio(filename));
    }

    /**
     * 下载电路模块文件/cae文件用
     * @param filepath
     * @param response
     * @throws Exception
     */
    @ApiOperation("从minio下载文件")
    @GetMapping("/download")
    public void download(@RequestParam("filepath") String filepath,
                         @RequestParam("token") String token,
                         HttpServletResponse response) throws Exception {
        if (tokenService.getLoginUser(token) == null){
            log.error("当前token[{}]无权限访问文件，无法根据token查询到用户",token);
            throw new ServiceException("当前无权限访问文件");
        }
        downloadMinioFile(filepath, response);
    }

    @ApiOperation("从minio下载文件（版本更新）")
    @GetMapping("/downloadNewVerFile")
    public void downloadNewVerFile(@RequestParam("filepath") String filepath,
                         HttpServletResponse response) throws Exception {
        if (StringUtils.isEmpty(versionPath)){
            // 默认值
            versionPath = "/edsta/updatePackages";
        }
        if (!filepath.contains(versionPath)){
            // 如果不包含指定的文件更新补丁路径，则认为是恶意提交，抛出异常
            throw new ServiceException("更新补丁文件路径有误，请联系管理员");
        }
        downloadMinioFile(filepath, response);
    }

    private void downloadMinioFile(String filepath, HttpServletResponse response) throws Exception {
        Path path = Paths.get(filepath);
        String bucketName = path.subpath(0, 1).toString();
        String filename = path.subpath(1, path.getNameCount()).toString();

        // 需要将路径替换为/aaa/aaa格式
        if (filename.contains("\\")) {
            // 因为minio部署在Linux上，理论上只支持/aaa/aaa格式
            filename = StringUtils.replace(filename, "\\", "/");
        }
        MinioUtils minioUtils = new MinioUtils(client, bucketName);
        minioUtils.downloadByHttp(filename, response);
    }
}